Setup

Read in dependencies

library(readr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
library(purrr)
library(ggbeeswarm)
library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(stringr)
library(jsonlite)

Attaching package: ‘jsonlite’

The following object is masked from ‘package:purrr’:

    flatten
library(forcats)
library(ggbreak)
ggbreak v0.1.3 Learn more at https://yulab-smu.top/


If you use ggbreak in published research, please cite the following paper:

S Xu, M Chen, T Feng, L Zhan, L Zhou, G Yu. Use ggbreak to effectively utilize plotting space to deal with large datasets and
outliers. Frontiers in Genetics. 2021, 12:774846. doi: 10.3389/fgene.2021.774846 
library(cowplot)
library(tidyr)
library(hexbin)

Read in data

# Map antiSMASH classes to their categories
bgc_class <- read_tsv("./data/2025-01-16-1256-bgc_class_ref.tsv") %>% select(!owner_id)
Rows: 87 Columns: 5── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): class_name, full_name, bgc_category
dbl (2): bgc_class_id, owner_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
class_to_cat <- bgc_class$bgc_category
names(class_to_cat) <- bgc_class$class_name

# NCBI Taxonomy data for Cyanobacteriota assemblies
cyano_asm_tax <- read_tsv("data/cyano_asm_tax.tsv")
Rows: 6108 Columns: 11── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (9): assembly_accession, tax_name, superkingdom, phylum, class, order, family, genus, species
dbl (1): tax_id
lgl (1): kingdomError: no more error handlers available (recursive errors?); invoking 'abort' restart
Warning in (function (bibtype, textVersion, header = NULL, footer = NULL,  :
  type 29 is unimplemented in 'type2char'
Error in (function (bibtype, textVersion, header = NULL, footer = NULL,  : 
  INTEGER() can only be applied to a 'integer', not a 'unknown type #29'

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# bgc_tax <- read_tsv("./data/2025-01-16-1522-cyano_bgc_tax.tsv")

# From SMC: All antiSMASH BGC calls for Cyanobacteriota genomes
# Need to clean a bit and add categories
regions <- read_tsv("./data/2025-02-26-1456-cyano_as_regions.tsv")
Rows: 32112 Columns: 21── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (10): contig_name, region_class, accession_id, data_source_description, tax_phylum, tax_class, tax_order, tax_family, tax_genus, tax_species
dbl  (9): region_gene_id, bgc_id, region_length, region_start_nt, region_end_nt, bgc_annotation_id, smc_id, size_bp, n_scaffolds
lgl  (2): region_category, contig_edge
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Table of NCBI assemblies at "chromosome" or "complete" quality levels
ncbi_hiq_meta <- read_tsv("data/ncbi_cyano_HiQualityGenomes_metadata.tsv")
Rows: 971 Columns: 25── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (11): Assembly Accession, Assembly Name, Organism Name, ANI Check status, Organism Infraspecific Names Strain, Organism Infraspecific Names...
dbl   (9): Organism Taxonomic ID, Assembly Stats Total Sequence Length, Assembly Stats Total Number of Chromosomes, Assembly Stats Contig N50, A...
lgl   (4): Organism Infraspecific Names Breed, Organism Infraspecific Names Cultivar, Organism Infraspecific Names Ecotype, Organism Infraspecif...
date  (1): Assembly Release Date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Clean data

# Convert 'class' to vector, add in 'category' as vector, order levels of classes and categories
regions <- regions %>%
    mutate(classes = map(region_class, function(class_string) (
        if ( str_starts(class_string, fixed("[")) )
            fromJSON(class_string)
        else
            c(class_string)
    ))) %>% 
    mutate(categories = classes %>% map(function(cls_vec) (
        map_vec(cls_vec, function(cls_str) class_to_cat[[cls_str]]) %>% unique() %>% sort())
    )) %>%
    mutate(cats_str = categories %>% map_chr(function(x) str_flatten(x, collapse = ", "))) %>%
    add_count(cats_str) %>% 
    mutate(
        cats_str = forcats::fct_reorder(cats_str, desc(n)),
        class_str = classes %>% map_chr(function(x) str_flatten(x, collapse = ", "))
    )

regions

Explore data

How fragmented are the full set of genomes, and how does that impact BGC counts?

regions %>%
    group_by(smc_id, n_scaffolds, contig_edge) %>%
    summarize(n_bgcs = n()) %>%
    ungroup() %>%
    ggplot(aes(x = n_scaffolds, y = n_bgcs)) +
        stat_bin_hex(bins = 50) +
        scale_x_log10(breaks = breaks_log()) +
        guides(fill = guide_colorbar(title = "# Genomes")) +
        facet_wrap(. ~ contig_edge, ncol = 1, labeller = as_labeller(c("FALSE" = "BGCs not on contig edge", "TRUE" = "BGCs on contig edge"))) +
        theme_bw() +
        ggtitle('Fragmented genomes have inflated BGC counts', subtitle = 'Full dataset')
`summarise()` has grouped output by 'smc_id', 'n_scaffolds'. You can override using the `.groups` argument.

How does the proportion of BGCs off/on a contig edge change if we filter for high-quality genomes?

filters_df <- bind_rows(
    regions %>%
        group_by(contig_edge) %>%
        summarize(filter = "Unfiltered", n = n()) %>%
        ungroup() %>%
        mutate(pct = 100 * n / sum(n)),
    regions %>%
        semi_join(ncbi_hiq_meta, by = join_by(accession_id == `Assembly Accession`)) %>%
        group_by(contig_edge) %>%
        summarize(filter = "Complete/Chromosome", n = n()) %>%
        mutate(pct = 100 * n / sum(n)),
    regions %>%
        filter(n_scaffolds < 30) %>%
        group_by(contig_edge) %>%
        summarize(filter = "< 30 Scaffolds", n = n()) %>%
        mutate(pct = 100 * n / sum(n)),
    regions %>%
        filter(n_scaffolds < 50) %>%
        group_by(contig_edge) %>%
        summarize(filter = "< 50 Scaffolds", n = n()) %>%
        mutate(pct = 100 * n / sum(n)),
    regions %>%
        filter(n_scaffolds < 100) %>%
        group_by(contig_edge) %>%
        summarize(filter = "< 100 Scaffolds", n = n()) %>%
        mutate(pct = 100 * n / sum(n)),
)

ggplot(filters_df, aes(x = filter, y = n)) +
    geom_col(aes(fill = fct_rev(as_factor(contig_edge))), position = position_stack()) +
    scale_x_discrete(name = "", limits = c("Unfiltered", "< 100 Scaffolds", "< 50 Scaffolds", "< 30 Scaffolds", "Complete/Chromosome")) +
    scale_y_continuous(name = "Number of BGCs", labels = label_comma()) +
    scale_fill_manual(name = "BGC on contig edge", values = c("gray80", "black")) +
    theme_classic() +
    coord_flip()



# filter            off / on contig edge
# n_scaffolds < 30  5415 /  513 (91% off)
# n_scaffolds < 50  6320 / 1203 (84% off)
# n_scaffolds < 100 8176 / 3056 (73% off)
# only HiQ          2885 / 68 (98%)

Filter the dataset to high-quality genomes

regions <- regions %>%
    semi_join(ncbi_hiq_meta, by = join_by(accession_id == `Assembly Accession`))

regions %>%
    group_by(smc_id) %>%
    mutate(n_bgcs = n()) %>%
    select(tax_genus, n_bgcs, n_scaffolds) %>%
    distinct() %>%
    ggplot(aes(x = n_scaffolds, y = n_bgcs)) +
        stat_bin_hex(bins = 50) +
        # geom_point(alpha = 0.3, pch=21) +
        # geom_beeswarm() +
        scale_x_log10(breaks = breaks_log()) +
        guides(fill = guide_colorbar(title = "# Genomes")) +
        # scale_x_continuous(transform = transform_pseudo_log(base=10), breaks = breaks_log())
        theme_bw()
Adding missing grouping variables: `smc_id`

Analyze data

Summary statistics of BGC length across BGC categories

region_summary <- regions %>%
    group_by(cats_str) %>%
    mutate(n = n()) %>%
    group_by(cats_str, n) %>%
    summarize_at(
        .vars = vars(region_length), 
        .funs = list(
            min_len = min, 
            max_len = max, 
            mean_len = mean, 
            median_len = median, 
            sd = sd
            )
        ) %>%
    arrange(desc(n))

region_summary
write_tsv(region_summary, "./region_summary.tsv")

# Purely informational plot -- Not needed for pub (since we have the table for it)
category_counts <- region_summary %>% 
    ggplot(aes(y = reorder(cats_str, desc(n)))) +
        geom_col(aes(x = n)) +
        scale_x_continuous(name = "BGC count", breaks = breaks_width(100)) +
        scale_y_discrete(name = "BGC Category") +
        theme_bw() +
        ggtitle('Number of BGCs in dataset, divided by category')
category_counts
ggsave("./figs/svg/category_counts.svg", category_counts, device = "svg")
Saving 7.29 x 4.51 in image
ggsave("./figs/png/category_counts.png", category_counts, device = "png")
Saving 7.29 x 4.51 in image

Number of BGCs by category - lumping all hybrids except NRPS-PKS

# Lump any hybrid category with fewer than 80 BGCs into an "all other" category
region_summary_lumped <- region_summary %>% 
    mutate(
        group = if_else(n < 80, "All other hybrids", cats_str),
        group = group %>% fct_reorder(n)
    )

# Keep a reference DF handy for which categories got lumped
lump_groups <- region_summary_lumped %>% select(cats_str, group)

# Use the MIBiG / antiSMASH coloring scheme
cat_colors <- c(
    "Polyketide" = "#f4a460",
    "NRP" = "#2e8b57",
    "RiPP" = "#4169e1",
    "Terpene" = "purple",
    "Saccharide" = "#deb887",
    "Other" = "#191970",
    "NRP, Polyketide" = "lightsteelblue",
    "All other hybrids" = "gray50"
)

# Plot it
lumped_category_counts <- region_summary_lumped %>% 
    ggplot(aes(y = reorder(group, n))) +
        geom_col(aes(x = n, fill = group)) +
        scale_x_continuous(name = "BGC count", breaks = breaks_extended()) +
        scale_y_discrete(name = "BGC Category") +
        scale_fill_manual(values = cat_colors) +
        theme_bw() +
        guides(fill = "none")
lumped_category_counts
ggsave("./figs/svg/category_counts_lumped.svg", lumped_category_counts, device = "svg")
Saving 7.29 x 4.51 in image
ggsave("./figs/png/category_counts_lumped.png", lumped_category_counts, device = "png")
Saving 7.29 x 4.51 in image

Distribution of BGC lengths by category (or combination of categories)

my_breaks <- c(1, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000)

regions_lumped <- regions %>% left_join(lump_groups, by = 'cats_str')


region_hist <- ggplot(regions_lumped, aes(
    x = region_length / 1000,
    # y = categories
)) + 
    geom_histogram(aes(fill=group), bins=50) +
    scale_x_log10(name = "BGC length (kb)", guide = "axis_logticks", breaks = breaks_log(), labels = label_comma()) +
    scale_y_continuous(name = "BGC count", breaks = breaks_extended(), labels = label_comma()) +
    scale_fill_manual(values = cat_colors) +
    facet_grid(rows = vars(cats_str), scales = "free_y") +
    theme_bw() + 
    theme(strip.text.y.right = element_text(angle = 0)) +
    guides(fill = FALSE)
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.
region_hist

ggsave("./figs/svg/region_hist.svg", region_hist, device = "svg")
Saving 10 x 10 in image
ggsave("./figs/png/region_hist.png", region_hist, device = "png")
Saving 10 x 10 in image

Same, but lump hybrid categories together, except for NRPS-PKS

region_hist_lumped <- regions_lumped %>%
    filter(group != "All other hybrids") %>%
    ggplot(aes(x = region_length / 1000)) + 
        geom_histogram(aes(fill=group), bins=50) +
        scale_x_log10(name = "BGC length (kb)", guide = "axis_logticks", limits = c(1, NA), breaks = c(1, 5, 10, 50, 100, 200)) +
        scale_y_continuous(name = "BGC count", breaks = breaks_extended(n = 3)) +
        scale_fill_manual(values = cat_colors) +
        facet_wrap(vars(group), ncol = 1, scales = "free_y") +
        guides(fill = guide_legend(title = "BGC Category")) +
        theme_bw() + 
        theme(strip.background = element_blank(),
              strip.text = element_blank())
region_hist_lumped
ggsave("./figs/svg/region_hist_lumped.svg", region_hist_lumped, device = "svg")
Saving 7.29 x 4.51 in image
ggsave("./figs/png/region_hist_lumped.png", region_hist_lumped, device = "png")
Saving 7.29 x 4.51 in image

Total BGCs divided by tax_genus, colored by BGC category

tax_count <- regions_lumped %>%
    group_by(tax_genus, cats_str) %>%
    summarize(num_bgcs = n()) %>%
    mutate(tax_genus = tax_genus %>% fct_reorder(num_bgcs)) %>%
    left_join(lump_groups, by = "cats_str") #%>%
`summarise()` has grouped output by 'tax_genus'. You can override using the `.groups` argument.
    # group_by(tax_genus) %>%
    # summarize(tot_bgcs = sum(num_bgcs))

tax_count
p_all <- tax_count %>%
    group_by(tax_genus) %>% 
    filter(sum(num_bgcs) > 0) %>% 
    ggplot(aes(x = fct_infreq(tax_genus, w = num_bgcs))) +
        geom_col(aes(y = num_bgcs, fill = group), position = position_stack(reverse = TRUE)) +
        scale_y_continuous(name = "BGC count", breaks = breaks_extended()) +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        theme_bw() + 
        ggtitle("(all genera)")
p_all
ggsave("./figs/svg/genus_counts_all.svg", p_all, device = "svg")
Saving 7.29 x 4.51 in image
ggsave("./figs/png/genus_counts_all.png", p_all, device = "png")
Saving 7.29 x 4.51 in image

cyano_nohits <- read_tsv('data/ncbi_cyano_nohit_accs.txt', col_names=c('accession_id')) %>% 
    left_join(cyano_asm_tax, by = join_by(accession_id == assembly_accession))
Rows: 186 Columns: 1── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): accession_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cyano_nohit_genus_counts <- cyano_nohits %>%
    group_by(genus) %>%
    summarize(n_nohits = n()) %>%
    mutate(genus = replace_na(genus, "Unclassified")) %>%
    arrange(genus)

genomes_by_genus <- read_tsv('data/2025-02-25-1442-cyano_smc_src_counts_by_genus.tsv')
Rows: 172 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): tax_genus
dbl (1): n_sources
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
genomes_by_genus <- genomes_by_genus %>%
    left_join(cyano_nohit_genus_counts, by = join_by(tax_genus == genus)) %>%
    mutate(n_nohits = replace_na(n_nohits, 0)) %>%
    mutate(tot_genomes = n_nohits + n_sources, .keep = "unused")

BGCs per genome (labeled with total BGCs) with a side panel for genome count

df_plots <- tax_count %>% 
    left_join(genomes_by_genus, by = 'tax_genus') %>%
    mutate(bgc_dens = num_bgcs/tot_genomes) 

p_bgc_dens <- df_plots %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=bgc_dens))) +
        geom_col(aes(y = bgc_dens, fill = group), position = position_stack(reverse = TRUE)) +
        geom_text(
            aes(
                y = tot_bgc_dens, 
                x = tax_genus, 
                label = format(tot_bgcs, big.mark = ',', trim = TRUE)
            ),
            data = df_plots %>% 
                group_by(tax_genus) %>% 
                mutate(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>% 
                select(tax_genus, tot_bgc_dens, tot_bgcs) %>% 
                distinct(),
            position = position_dodge(0.9),
            hjust = -0.2
        ) +
        scale_y_continuous(name = "BGCs per genome", breaks = seq(0,55,5), limits = c(0,55)) +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        theme_bw() +
        theme(legend.position = "bottom")
# p_bgc_dens

genome_counts <- df_plots %>% 
    group_by(tax_genus, tot_genomes) %>% 
    summarize(
        tot_bgc_dens = sum(bgc_dens), 
        tot_bgcs = sum(num_bgcs)
    ) %>%
    arrange(tot_bgc_dens)
genome_counts

p_genome_ct <- genome_counts %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=tot_bgc_dens))) +
        geom_col(aes(y = tot_genomes)) +
        scale_y_continuous(trans = transform_pseudo_log(base=10), breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500, 1000)) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
# p_genome_ct

p1 <- plot_grid(p_bgc_dens, p_genome_ct, align = "h", rel_widths = c(2,1))
p1
ggsave("./figs/png/split.png", p1, width = 8, height = 11, device = "png")

Same thing but filter for genera with at least 5 genomes

df_plots <- tax_count %>% 
    left_join(genomes_by_genus, by = 'tax_genus') %>%
    mutate(bgc_dens = num_bgcs/tot_genomes) %>%
    filter(tot_genomes >= 5)

p_bgc_dens <- df_plots %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=bgc_dens))) +
        geom_col(aes(y = bgc_dens, fill = group), position = position_stack(reverse = TRUE)) +
        geom_text(
            aes(y = tot_bgc_dens, x = tax_genus, label = format(tot_bgcs, big.mark = ',', trim = TRUE)),
            data = df_plots %>% group_by(tax_genus) %>% mutate(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>% select(tax_genus, tot_bgc_dens, tot_bgcs) %>% distinct(),
            position = position_dodge(0.9),
            hjust = -0.2
        ) +
        scale_y_continuous(name = "BGCs per genome", breaks = seq(0,25,5), limits = c(0,20)) +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        theme_bw() +
        theme(legend.position = "bottom") +
        ggtitle('>= 5 genomes in genus')
# p_bgc_dens

genome_counts <- df_plots %>% 
    group_by(tax_genus, tot_genomes) %>% 
    summarize(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>%
    arrange(tot_bgc_dens)
genome_counts

p_genome_ct <- genome_counts %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=tot_bgc_dens))) +
        geom_col(aes(y = tot_genomes)) +
        scale_y_continuous(trans=scales::transform_pseudo_log(base=10), breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500, 1000)) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
# p_genome_ct

p2 <- plot_grid(p_bgc_dens, p_genome_ct, align = "h", rel_widths = c(2,1))
p2
ggsave("./figs/png/split_5genomes.png", p2, width = 8, height = 11, device = "png")

Same but instead filter for genera with at least 50 BGCs

df_plots <- tax_count %>% 
    left_join(genomes_by_genus, by = 'tax_genus') %>%
    mutate(bgc_dens = num_bgcs/tot_genomes) %>%
    group_by(tax_genus) %>% mutate(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>%
    filter(tot_bgcs >= 50)

p_bgc_dens <- df_plots %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=bgc_dens))) +
        geom_col(aes(y = bgc_dens, fill = group), position = position_stack(reverse = TRUE)) +
        geom_text(
            aes(y = tot_bgc_dens, x = tax_genus, label = format(tot_bgcs, big.mark = ',', trim = TRUE)),
            data = df_plots %>% select(tax_genus, tot_bgc_dens, tot_bgcs) %>% distinct(),
            position = position_dodge(0.9),
            hjust = -0.2
        ) +
        scale_y_continuous(name = "BGCs per genome", breaks = seq(0,25,5), limits = c(0,20)) +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        theme_bw() +
        theme(legend.position = "bottom") +
        ggtitle('>= 50 BGCs in genus')
# p_bgc_dens

genome_counts <- df_plots %>% 
    group_by(tax_genus, tot_genomes) %>% 
    summarize(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>%
    arrange(tot_bgc_dens)
genome_counts

p_genome_ct <- genome_counts %>%
    ggplot(aes(x = fct_infreq(tax_genus, w=tot_bgc_dens))) +
        geom_col(aes(y = tot_genomes)) +
        scale_y_continuous(trans=scales::transform_pseudo_log(base=10), breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500, 1000)) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
# p_genome_ct

p3 <- plot_grid(p_bgc_dens, p_genome_ct, align = "h", rel_widths = c(2,1))
p3
ggsave("./figs/png/split_50bgcs.png", p3, width = 8, height = 11, device = "png")

Plot the BGCs per genome (normalized to 100%) alongside number of BGCs per genus and genomes per genus

df_plots <- tax_count %>% 
    left_join(genomes_by_genus, by = 'tax_genus') %>%
    mutate(bgc_dens = num_bgcs/tot_genomes) %>%
    group_by(tax_genus) %>% mutate(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs))

p_bgc_dens <- df_plots %>%
    ggplot(aes(x = fct_rev(tax_genus))) +
        geom_col(aes(y = bgc_dens, fill = group), position = position_fill(reverse = TRUE)) +
        scale_y_continuous(name = "BGC proportion") +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        theme_bw() +
        theme(legend.position = "bottom")
# p_bgc_dens

p_bgc_ct <- df_plots %>% 
    ggplot(aes(x = fct_rev(tax_genus))) +
    geom_col(aes(y = tot_bgcs), data = df_plots %>% select(tax_genus, tot_bgcs, tot_bgc_dens) %>% distinct()) +
    scale_y_continuous(
        name = "BGC count", 
        trans = transform_pseudo_log(base = 10), 
        breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500)
    ) +
    coord_flip() +
    theme_bw() +
    theme(
        axis.title.y = element_blank(),
        axis.text.y = element_blank()
    )

genome_counts <- df_plots %>% 
    group_by(tax_genus, tot_genomes) %>% 
    summarize(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>%
    arrange(tot_bgc_dens)
`summarise()` has grouped output by 'tax_genus'. You can override using the `.groups` argument.
genome_counts

p_genome_ct <- genome_counts %>%
    ggplot(aes(x = fct_rev(tax_genus))) +
        geom_col(aes(y = tot_genomes)) +
        scale_y_continuous(
            name = "Genome count", 
            trans=transform_pseudo_log(base = 10), 
            breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500, 1000)
        ) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
# p_genome_ct

p4 <- plot_grid(p_bgc_dens, p_bgc_ct, p_genome_ct, align = "h", rel_widths = c(3,1,1), nrow=1)
p4
ggsave("./figs/png/split_proportional.png", p4, width = 8, height = 11, device = "png")

Plot the BGCs per genome (not normalized to 100%) alongside number of BGCs per genus and genomes per genus

df_plots <- tax_count %>% 
    left_join(genomes_by_genus, by = 'tax_genus') %>%
    mutate(bgc_dens = num_bgcs/tot_genomes) %>%
    group_by(tax_genus) %>% 
    mutate(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs))

p_bgc_dens <- df_plots %>%
    ggplot(aes(x = fct_reorder(tax_genus, tot_bgc_dens, .desc=T))) +
        geom_col(aes(y = bgc_dens, fill = group), position = position_stack(reverse = TRUE)) +
        scale_y_continuous(name = "BGCs per genome") +
        scale_x_discrete(name = "Genus") +
        scale_fill_manual(name = "BGC Category", values = cat_colors) +
        coord_flip() +
        guides(fill = guide_legend(position = "inside")) +
        theme_bw() +
        theme(legend.justification.inside = c(0.99,0.99))
# p_bgc_dens

p_bgc_ct <- df_plots %>% 
    ggplot(aes(x = fct_reorder(tax_genus, tot_bgc_dens, .desc=T))) +
        geom_col(aes(y = tot_bgcs), data = df_plots %>% select(tax_genus, tot_bgcs, tot_bgc_dens) %>% distinct()) +
        scale_y_continuous(
            name = "BGC count", 
            trans = transform_pseudo_log(base = 10), 
            breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500)
        ) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
    
genome_counts <- df_plots %>% 
    group_by(tax_genus, tot_genomes) %>% 
    summarize(tot_bgc_dens = sum(bgc_dens), tot_bgcs = sum(num_bgcs)) %>%
    arrange(tot_bgc_dens)
`summarise()` has grouped output by 'tax_genus'. You can override using the `.groups` argument.
genome_counts

p_genome_ct <- genome_counts %>%
    ggplot(aes(x = fct_reorder(tax_genus, tot_bgc_dens, .desc=T))) +
        geom_col(aes(y = tot_genomes)) +
        scale_y_continuous(name = "Genome count", trans=scales::transform_pseudo_log(base=10), breaks = c(0, 1, 5, 10, 20, 50, 100, 200, 500, 1000)) +
        coord_flip() +
        theme_bw() +
        theme(
            axis.title.y = element_blank(),
            axis.text.y = element_blank()
        )
# p_genome_ct

p5 <- plot_grid(p_bgc_dens, p_bgc_ct, p_genome_ct, align = "h", rel_widths = c(3,1,1), nrow=1)
df_plots
p5
ggsave("./figs/png/split_triple.png", p4, width = 8, height = 11, device = "png")

Create Figure for the manuscript

p_a <- p5 + ggtitle("")

p_b <- lumped_category_counts

p_c <- region_hist_lumped +
    guides(fill = "none")

bottom_row <- plot_grid(p_b, p_c, nrow = 1, labels = c("B", "C"), label_size = 12)
Warning: Removed 6 rows containing missing values or values outside the scale range (`geom_bar()`).
fig2 <- plot_grid(p_a, bottom_row, nrow = 2, labels = c("A", ""), label_size = 12, rel_heights = c(2,1))
fig2
ggsave("./figs/_fig2.png", fig2, width = 7, height = 7, device = "png")
ggsave("./figs/_fig2.pdf", fig2, width = 7, height = 7, device = "pdf")

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBTZXR1cAoKIyMgUmVhZCBpbiBkZXBlbmRlbmNpZXMKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZ2diZWVzd2FybSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShqc29ubGl0ZSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdnYnJlYWspCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeSh0aWR5cikKbGlicmFyeShoZXhiaW4pCmBgYAoKIyMgUmVhZCBpbiBkYXRhCgpgYGB7cn0KIyBNYXAgYW50aVNNQVNIIGNsYXNzZXMgdG8gdGhlaXIgY2F0ZWdvcmllcwpiZ2NfY2xhc3MgPC0gcmVhZF90c3YoIi4vZGF0YS8yMDI1LTAxLTE2LTEyNTYtYmdjX2NsYXNzX3JlZi50c3YiKSAlPiUgc2VsZWN0KCFvd25lcl9pZCkKY2xhc3NfdG9fY2F0IDwtIGJnY19jbGFzcyRiZ2NfY2F0ZWdvcnkKbmFtZXMoY2xhc3NfdG9fY2F0KSA8LSBiZ2NfY2xhc3MkY2xhc3NfbmFtZQoKIyBOQ0JJIFRheG9ub215IGRhdGEgZm9yIEN5YW5vYmFjdGVyaW90YSBhc3NlbWJsaWVzCmN5YW5vX2FzbV90YXggPC0gcmVhZF90c3YoImRhdGEvY3lhbm9fYXNtX3RheC50c3YiKQoKIyBiZ2NfdGF4IDwtIHJlYWRfdHN2KCIuL2RhdGEvMjAyNS0wMS0xNi0xNTIyLWN5YW5vX2JnY190YXgudHN2IikKCiMgRnJvbSBTTUM6IEFsbCBhbnRpU01BU0ggQkdDIGNhbGxzIGZvciBDeWFub2JhY3RlcmlvdGEgZ2Vub21lcwojIE5lZWQgdG8gY2xlYW4gYSBiaXQgYW5kIGFkZCBjYXRlZ29yaWVzCnJlZ2lvbnMgPC0gcmVhZF90c3YoIi4vZGF0YS8yMDI1LTAyLTI2LTE0NTYtY3lhbm9fYXNfcmVnaW9ucy50c3YiKQoKIyBUYWJsZSBvZiBOQ0JJIGFzc2VtYmxpZXMgYXQgImNocm9tb3NvbWUiIG9yICJjb21wbGV0ZSIgcXVhbGl0eSBsZXZlbHMKbmNiaV9oaXFfbWV0YSA8LSByZWFkX3RzdigiZGF0YS9uY2JpX2N5YW5vX0hpUXVhbGl0eUdlbm9tZXNfbWV0YWRhdGEudHN2IikKCmBgYAoKIyMgQ2xlYW4gZGF0YQoKYGBge3J9CiMgQ29udmVydCAnY2xhc3MnIHRvIHZlY3RvciwgYWRkIGluICdjYXRlZ29yeScgYXMgdmVjdG9yLCBvcmRlciBsZXZlbHMgb2YgY2xhc3NlcyBhbmQgY2F0ZWdvcmllcwpyZWdpb25zIDwtIHJlZ2lvbnMgJT4lCiAgICBtdXRhdGUoY2xhc3NlcyA9IG1hcChyZWdpb25fY2xhc3MsIGZ1bmN0aW9uKGNsYXNzX3N0cmluZykgKAogICAgICAgIGlmICggc3RyX3N0YXJ0cyhjbGFzc19zdHJpbmcsIGZpeGVkKCJbIikpICkKICAgICAgICAgICAgZnJvbUpTT04oY2xhc3Nfc3RyaW5nKQogICAgICAgIGVsc2UKICAgICAgICAgICAgYyhjbGFzc19zdHJpbmcpCiAgICApKSkgJT4lIAogICAgbXV0YXRlKGNhdGVnb3JpZXMgPSBjbGFzc2VzICU+JSBtYXAoZnVuY3Rpb24oY2xzX3ZlYykgKAogICAgICAgIG1hcF92ZWMoY2xzX3ZlYywgZnVuY3Rpb24oY2xzX3N0cikgY2xhc3NfdG9fY2F0W1tjbHNfc3RyXV0pICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpKQogICAgKSkgJT4lCiAgICBtdXRhdGUoY2F0c19zdHIgPSBjYXRlZ29yaWVzICU+JSBtYXBfY2hyKGZ1bmN0aW9uKHgpIHN0cl9mbGF0dGVuKHgsIGNvbGxhcHNlID0gIiwgIikpKSAlPiUKICAgIGFkZF9jb3VudChjYXRzX3N0cikgJT4lIAogICAgbXV0YXRlKAogICAgICAgIGNhdHNfc3RyID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIoY2F0c19zdHIsIGRlc2MobikpLAogICAgICAgIGNsYXNzX3N0ciA9IGNsYXNzZXMgJT4lIG1hcF9jaHIoZnVuY3Rpb24oeCkgc3RyX2ZsYXR0ZW4oeCwgY29sbGFwc2UgPSAiLCAiKSkKICAgICkKCnJlZ2lvbnMKYGBgCgojIyBFeHBsb3JlIGRhdGEKCkhvdyBmcmFnbWVudGVkIGFyZSB0aGUgZnVsbCBzZXQgb2YgZ2Vub21lcywgYW5kIGhvdyBkb2VzIHRoYXQgaW1wYWN0IEJHQyBjb3VudHM/CgpgYGB7ciBmaWcuYXNwPTAuNjIqMn0KcmVnaW9ucyAlPiUKICAgIGdyb3VwX2J5KHNtY19pZCwgbl9zY2FmZm9sZHMsIGNvbnRpZ19lZGdlKSAlPiUKICAgIHN1bW1hcml6ZShuX2JnY3MgPSBuKCkpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gbl9zY2FmZm9sZHMsIHkgPSBuX2JnY3MpKSArCiAgICAgICAgc3RhdF9iaW5faGV4KGJpbnMgPSA1MCkgKwogICAgICAgIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gYnJlYWtzX2xvZygpKSArCiAgICAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9jb2xvcmJhcih0aXRsZSA9ICIjIEdlbm9tZXMiKSkgKwogICAgICAgIGZhY2V0X3dyYXAoLiB+IGNvbnRpZ19lZGdlLCBuY29sID0gMSwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKCJGQUxTRSIgPSAiQkdDcyBub3Qgb24gY29udGlnIGVkZ2UiLCAiVFJVRSIgPSAiQkdDcyBvbiBjb250aWcgZWRnZSIpKSkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIGdndGl0bGUoJ0ZyYWdtZW50ZWQgZ2Vub21lcyBoYXZlIGluZmxhdGVkIEJHQyBjb3VudHMnLCBzdWJ0aXRsZSA9ICdGdWxsIGRhdGFzZXQnKQpgYGAKCkhvdyBkb2VzIHRoZSBwcm9wb3J0aW9uIG9mIEJHQ3Mgb2ZmL29uIGEgY29udGlnIGVkZ2UgY2hhbmdlIGlmIHdlIGZpbHRlciBmb3IgaGlnaC1xdWFsaXR5IGdlbm9tZXM/CgpgYGB7cn0KZmlsdGVyc19kZiA8LSBiaW5kX3Jvd3MoCiAgICByZWdpb25zICU+JQogICAgICAgIGdyb3VwX2J5KGNvbnRpZ19lZGdlKSAlPiUKICAgICAgICBzdW1tYXJpemUoZmlsdGVyID0gIlVuZmlsdGVyZWQiLCBuID0gbigpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgbXV0YXRlKHBjdCA9IDEwMCAqIG4gLyBzdW0obikpLAogICAgcmVnaW9ucyAlPiUKICAgICAgICBzZW1pX2pvaW4obmNiaV9oaXFfbWV0YSwgYnkgPSBqb2luX2J5KGFjY2Vzc2lvbl9pZCA9PSBgQXNzZW1ibHkgQWNjZXNzaW9uYCkpICU+JQogICAgICAgIGdyb3VwX2J5KGNvbnRpZ19lZGdlKSAlPiUKICAgICAgICBzdW1tYXJpemUoZmlsdGVyID0gIkNvbXBsZXRlL0Nocm9tb3NvbWUiLCBuID0gbigpKSAlPiUKICAgICAgICBtdXRhdGUocGN0ID0gMTAwICogbiAvIHN1bShuKSksCiAgICByZWdpb25zICU+JQogICAgICAgIGZpbHRlcihuX3NjYWZmb2xkcyA8IDMwKSAlPiUKICAgICAgICBncm91cF9ieShjb250aWdfZWRnZSkgJT4lCiAgICAgICAgc3VtbWFyaXplKGZpbHRlciA9ICI8IDMwIFNjYWZmb2xkcyIsIG4gPSBuKCkpICU+JQogICAgICAgIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pKSwKICAgIHJlZ2lvbnMgJT4lCiAgICAgICAgZmlsdGVyKG5fc2NhZmZvbGRzIDwgNTApICU+JQogICAgICAgIGdyb3VwX2J5KGNvbnRpZ19lZGdlKSAlPiUKICAgICAgICBzdW1tYXJpemUoZmlsdGVyID0gIjwgNTAgU2NhZmZvbGRzIiwgbiA9IG4oKSkgJT4lCiAgICAgICAgbXV0YXRlKHBjdCA9IDEwMCAqIG4gLyBzdW0obikpLAogICAgcmVnaW9ucyAlPiUKICAgICAgICBmaWx0ZXIobl9zY2FmZm9sZHMgPCAxMDApICU+JQogICAgICAgIGdyb3VwX2J5KGNvbnRpZ19lZGdlKSAlPiUKICAgICAgICBzdW1tYXJpemUoZmlsdGVyID0gIjwgMTAwIFNjYWZmb2xkcyIsIG4gPSBuKCkpICU+JQogICAgICAgIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pKSwKKQoKZ2dwbG90KGZpbHRlcnNfZGYsIGFlcyh4ID0gZmlsdGVyLCB5ID0gbikpICsKICAgIGdlb21fY29sKGFlcyhmaWxsID0gZmN0X3Jldihhc19mYWN0b3IoY29udGlnX2VkZ2UpKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2soKSkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIiIsIGxpbWl0cyA9IGMoIlVuZmlsdGVyZWQiLCAiPCAxMDAgU2NhZmZvbGRzIiwgIjwgNTAgU2NhZmZvbGRzIiwgIjwgMzAgU2NhZmZvbGRzIiwgIkNvbXBsZXRlL0Nocm9tb3NvbWUiKSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiTnVtYmVyIG9mIEJHQ3MiLCBsYWJlbHMgPSBsYWJlbF9jb21tYSgpKSArCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkJHQyBvbiBjb250aWcgZWRnZSIsIHZhbHVlcyA9IGMoImdyYXk4MCIsICJibGFjayIpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgY29vcmRfZmxpcCgpCgoKIyBmaWx0ZXIgICAgICAgICAgICBvZmYgLyBvbiBjb250aWcgZWRnZQojIG5fc2NhZmZvbGRzIDwgMzAgIDU0MTUgLyAgNTEzICg5MSUgb2ZmKQojIG5fc2NhZmZvbGRzIDwgNTAgIDYzMjAgLyAxMjAzICg4NCUgb2ZmKQojIG5fc2NhZmZvbGRzIDwgMTAwIDgxNzYgLyAzMDU2ICg3MyUgb2ZmKQojIG9ubHkgSGlRICAgICAgICAgIDI4ODUgLyA2OCAoOTglKQpgYGAKCiMjIEZpbHRlciB0aGUgZGF0YXNldCB0byBoaWdoLXF1YWxpdHkgZ2Vub21lcwoKYGBge3J9CnJlZ2lvbnMgPC0gcmVnaW9ucyAlPiUKICAgIHNlbWlfam9pbihuY2JpX2hpcV9tZXRhLCBieSA9IGpvaW5fYnkoYWNjZXNzaW9uX2lkID09IGBBc3NlbWJseSBBY2Nlc3Npb25gKSkKCnJlZ2lvbnMgJT4lCiAgICBncm91cF9ieShzbWNfaWQpICU+JQogICAgbXV0YXRlKG5fYmdjcyA9IG4oKSkgJT4lCiAgICBzZWxlY3QodGF4X2dlbnVzLCBuX2JnY3MsIG5fc2NhZmZvbGRzKSAlPiUKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBuX3NjYWZmb2xkcywgeSA9IG5fYmdjcykpICsKICAgICAgICBzdGF0X2Jpbl9oZXgoYmlucyA9IDUwKSArCiAgICAgICAgIyBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBwY2g9MjEpICsKICAgICAgICAjIGdlb21fYmVlc3dhcm0oKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMChicmVha3MgPSBicmVha3NfbG9nKCkpICsKICAgICAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2NvbG9yYmFyKHRpdGxlID0gIiMgR2Vub21lcyIpKSArCiAgICAgICAgIyBzY2FsZV94X2NvbnRpbnVvdXModHJhbnNmb3JtID0gdHJhbnNmb3JtX3BzZXVkb19sb2coYmFzZT0xMCksIGJyZWFrcyA9IGJyZWFrc19sb2coKSkKICAgICAgICB0aGVtZV9idygpCmBgYAoKIyBBbmFseXplIGRhdGEKClN1bW1hcnkgc3RhdGlzdGljcyBvZiBCR0MgbGVuZ3RoIGFjcm9zcyBCR0MgY2F0ZWdvcmllcwoKYGBge3J9CnJlZ2lvbl9zdW1tYXJ5IDwtIHJlZ2lvbnMgJT4lCiAgICBncm91cF9ieShjYXRzX3N0cikgJT4lCiAgICBtdXRhdGUobiA9IG4oKSkgJT4lCiAgICBncm91cF9ieShjYXRzX3N0ciwgbikgJT4lCiAgICBzdW1tYXJpemVfYXQoCiAgICAgICAgLnZhcnMgPSB2YXJzKHJlZ2lvbl9sZW5ndGgpLCAKICAgICAgICAuZnVucyA9IGxpc3QoCiAgICAgICAgICAgIG1pbl9sZW4gPSBtaW4sIAogICAgICAgICAgICBtYXhfbGVuID0gbWF4LCAKICAgICAgICAgICAgbWVhbl9sZW4gPSBtZWFuLCAKICAgICAgICAgICAgbWVkaWFuX2xlbiA9IG1lZGlhbiwgCiAgICAgICAgICAgIHNkID0gc2QKICAgICAgICAgICAgKQogICAgICAgICkgJT4lCiAgICBhcnJhbmdlKGRlc2MobikpCgpyZWdpb25fc3VtbWFyeQp3cml0ZV90c3YocmVnaW9uX3N1bW1hcnksICIuL3JlZ2lvbl9zdW1tYXJ5LnRzdiIpCgojIFB1cmVseSBpbmZvcm1hdGlvbmFsIHBsb3QgLS0gTm90IG5lZWRlZCBmb3IgcHViIChzaW5jZSB3ZSBoYXZlIHRoZSB0YWJsZSBmb3IgaXQpCmNhdGVnb3J5X2NvdW50cyA8LSByZWdpb25fc3VtbWFyeSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHkgPSByZW9yZGVyKGNhdHNfc3RyLCBkZXNjKG4pKSkpICsKICAgICAgICBnZW9tX2NvbChhZXMoeCA9IG4pKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiQkdDIGNvdW50IiwgYnJlYWtzID0gYnJlYWtzX3dpZHRoKDEwMCkpICsKICAgICAgICBzY2FsZV95X2Rpc2NyZXRlKG5hbWUgPSAiQkdDIENhdGVnb3J5IikgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIGdndGl0bGUoJ051bWJlciBvZiBCR0NzIGluIGRhdGFzZXQsIGRpdmlkZWQgYnkgY2F0ZWdvcnknKQpjYXRlZ29yeV9jb3VudHMKZ2dzYXZlKCIuL2ZpZ3Mvc3ZnL2NhdGVnb3J5X2NvdW50cy5zdmciLCBjYXRlZ29yeV9jb3VudHMsIGRldmljZSA9ICJzdmciKQpnZ3NhdmUoIi4vZmlncy9wbmcvY2F0ZWdvcnlfY291bnRzLnBuZyIsIGNhdGVnb3J5X2NvdW50cywgZGV2aWNlID0gInBuZyIpCmBgYAoKTnVtYmVyIG9mIEJHQ3MgYnkgY2F0ZWdvcnkgLSBsdW1waW5nIGFsbCBoeWJyaWRzIGV4Y2VwdCBOUlBTLVBLUwoKYGBge3J9CiMgTHVtcCBhbnkgaHlicmlkIGNhdGVnb3J5IHdpdGggZmV3ZXIgdGhhbiA4MCBCR0NzIGludG8gYW4gImFsbCBvdGhlciIgY2F0ZWdvcnkKcmVnaW9uX3N1bW1hcnlfbHVtcGVkIDwtIHJlZ2lvbl9zdW1tYXJ5ICU+JSAKICAgIG11dGF0ZSgKICAgICAgICBncm91cCA9IGlmX2Vsc2UobiA8IDgwLCAiQWxsIG90aGVyIGh5YnJpZHMiLCBjYXRzX3N0ciksCiAgICAgICAgZ3JvdXAgPSBncm91cCAlPiUgZmN0X3Jlb3JkZXIobikKICAgICkKCiMgS2VlcCBhIHJlZmVyZW5jZSBERiBoYW5keSBmb3Igd2hpY2ggY2F0ZWdvcmllcyBnb3QgbHVtcGVkCmx1bXBfZ3JvdXBzIDwtIHJlZ2lvbl9zdW1tYXJ5X2x1bXBlZCAlPiUgc2VsZWN0KGNhdHNfc3RyLCBncm91cCkKCiMgVXNlIHRoZSBNSUJpRyAvIGFudGlTTUFTSCBjb2xvcmluZyBzY2hlbWUKY2F0X2NvbG9ycyA8LSBjKAogICAgIlBvbHlrZXRpZGUiID0gIiNmNGE0NjAiLAogICAgIk5SUCIgPSAiIzJlOGI1NyIsCiAgICAiUmlQUCIgPSAiIzQxNjllMSIsCiAgICAiVGVycGVuZSIgPSAicHVycGxlIiwKICAgICJTYWNjaGFyaWRlIiA9ICIjZGViODg3IiwKICAgICJPdGhlciIgPSAiIzE5MTk3MCIsCiAgICAiTlJQLCBQb2x5a2V0aWRlIiA9ICJsaWdodHN0ZWVsYmx1ZSIsCiAgICAiQWxsIG90aGVyIGh5YnJpZHMiID0gImdyYXk1MCIKKQoKIyBQbG90IGl0Cmx1bXBlZF9jYXRlZ29yeV9jb3VudHMgPC0gcmVnaW9uX3N1bW1hcnlfbHVtcGVkICU+JSAKICAgIGdncGxvdChhZXMoeSA9IHJlb3JkZXIoZ3JvdXAsIG4pKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh4ID0gbiwgZmlsbCA9IGdyb3VwKSkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIkJHQyBjb3VudCIsIGJyZWFrcyA9IGJyZWFrc19leHRlbmRlZCgpKSArCiAgICAgICAgc2NhbGVfeV9kaXNjcmV0ZShuYW1lID0gIkJHQyBDYXRlZ29yeSIpICsKICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjYXRfY29sb3JzKSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgZ3VpZGVzKGZpbGwgPSAibm9uZSIpCmx1bXBlZF9jYXRlZ29yeV9jb3VudHMKZ2dzYXZlKCIuL2ZpZ3Mvc3ZnL2NhdGVnb3J5X2NvdW50c19sdW1wZWQuc3ZnIiwgbHVtcGVkX2NhdGVnb3J5X2NvdW50cywgZGV2aWNlID0gInN2ZyIpCmdnc2F2ZSgiLi9maWdzL3BuZy9jYXRlZ29yeV9jb3VudHNfbHVtcGVkLnBuZyIsIGx1bXBlZF9jYXRlZ29yeV9jb3VudHMsIGRldmljZSA9ICJwbmciKQpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBCR0MgbGVuZ3RocyBieSBjYXRlZ29yeSAob3IgY29tYmluYXRpb24gb2YgY2F0ZWdvcmllcykKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KbXlfYnJlYWtzIDwtIGMoMSwgMTAwMCwgMjAwMCwgNTAwMCwgMTAwMDAsIDIwMDAwLCA1MDAwMCwgMTAwMDAwLCAyMDAwMDAsIDUwMDAwMCkKCnJlZ2lvbnNfbHVtcGVkIDwtIHJlZ2lvbnMgJT4lIGxlZnRfam9pbihsdW1wX2dyb3VwcywgYnkgPSAnY2F0c19zdHInKQoKCnJlZ2lvbl9oaXN0IDwtIGdncGxvdChyZWdpb25zX2x1bXBlZCwgYWVzKAogICAgeCA9IHJlZ2lvbl9sZW5ndGggLyAxMDAwLAogICAgIyB5ID0gY2F0ZWdvcmllcwopKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGw9Z3JvdXApLCBiaW5zPTUwKSArCiAgICBzY2FsZV94X2xvZzEwKG5hbWUgPSAiQkdDIGxlbmd0aCAoa2IpIiwgZ3VpZGUgPSAiYXhpc19sb2d0aWNrcyIsIGJyZWFrcyA9IGJyZWFrc19sb2coKSwgbGFiZWxzID0gbGFiZWxfY29tbWEoKSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiQkdDIGNvdW50IiwgYnJlYWtzID0gYnJlYWtzX2V4dGVuZGVkKCksIGxhYmVscyA9IGxhYmVsX2NvbW1hKCkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNhdF9jb2xvcnMpICsKICAgIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoY2F0c19zdHIpLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgdGhlbWVfYncoKSArIAogICAgdGhlbWUoc3RyaXAudGV4dC55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCkpICsKICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpCgpyZWdpb25faGlzdApnZ3NhdmUoIi4vZmlncy9zdmcvcmVnaW9uX2hpc3Quc3ZnIiwgcmVnaW9uX2hpc3QsIGRldmljZSA9ICJzdmciKQpnZ3NhdmUoIi4vZmlncy9wbmcvcmVnaW9uX2hpc3QucG5nIiwgcmVnaW9uX2hpc3QsIGRldmljZSA9ICJwbmciKQpgYGAKClNhbWUsIGJ1dCBsdW1wIGh5YnJpZCBjYXRlZ29yaWVzIHRvZ2V0aGVyLCBleGNlcHQgZm9yIE5SUFMtUEtTCgpgYGB7cn0KcmVnaW9uX2hpc3RfbHVtcGVkIDwtIHJlZ2lvbnNfbHVtcGVkICU+JQogICAgZmlsdGVyKGdyb3VwICE9ICJBbGwgb3RoZXIgaHlicmlkcyIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gcmVnaW9uX2xlbmd0aCAvIDEwMDApKSArIAogICAgICAgIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPWdyb3VwKSwgYmlucz01MCkgKwogICAgICAgIHNjYWxlX3hfbG9nMTAobmFtZSA9ICJCR0MgbGVuZ3RoIChrYikiLCBndWlkZSA9ICJheGlzX2xvZ3RpY2tzIiwgbGltaXRzID0gYygxLCBOQSksIGJyZWFrcyA9IGMoMSwgNSwgMTAsIDUwLCAxMDAsIDIwMCkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJCR0MgY291bnQiLCBicmVha3MgPSBicmVha3NfZXh0ZW5kZWQobiA9IDMpKSArCiAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY2F0X2NvbG9ycykgKwogICAgICAgIGZhY2V0X3dyYXAodmFycyhncm91cCksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkJHQyBDYXRlZ29yeSIpKSArCiAgICAgICAgdGhlbWVfYncoKSArIAogICAgICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkKcmVnaW9uX2hpc3RfbHVtcGVkCmdnc2F2ZSgiLi9maWdzL3N2Zy9yZWdpb25faGlzdF9sdW1wZWQuc3ZnIiwgcmVnaW9uX2hpc3RfbHVtcGVkLCBkZXZpY2UgPSAic3ZnIikKZ2dzYXZlKCIuL2ZpZ3MvcG5nL3JlZ2lvbl9oaXN0X2x1bXBlZC5wbmciLCByZWdpb25faGlzdF9sdW1wZWQsIGRldmljZSA9ICJwbmciKQpgYGAKClRvdGFsIEJHQ3MgZGl2aWRlZCBieSB0YXhfZ2VudXMsIGNvbG9yZWQgYnkgQkdDIGNhdGVnb3J5CgpgYGB7cn0KdGF4X2NvdW50IDwtIHJlZ2lvbnNfbHVtcGVkICU+JQogICAgZ3JvdXBfYnkodGF4X2dlbnVzLCBjYXRzX3N0cikgJT4lCiAgICBzdW1tYXJpemUobnVtX2JnY3MgPSBuKCkpICU+JQogICAgbXV0YXRlKHRheF9nZW51cyA9IHRheF9nZW51cyAlPiUgZmN0X3Jlb3JkZXIobnVtX2JnY3MpKSAlPiUKICAgIGxlZnRfam9pbihsdW1wX2dyb3VwcywgYnkgPSAiY2F0c19zdHIiKSAjJT4lCiAgICAjIGdyb3VwX2J5KHRheF9nZW51cykgJT4lCiAgICAjIHN1bW1hcml6ZSh0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpCgp0YXhfY291bnQKYGBgCgpgYGB7cn0KcF9hbGwgPC0gdGF4X2NvdW50ICU+JQogICAgZ3JvdXBfYnkodGF4X2dlbnVzKSAlPiUgCiAgICBmaWx0ZXIoc3VtKG51bV9iZ2NzKSA+IDApICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGZjdF9pbmZyZXEodGF4X2dlbnVzLCB3ID0gbnVtX2JnY3MpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gbnVtX2JnY3MsIGZpbGwgPSBncm91cCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiQkdDIGNvdW50IiwgYnJlYWtzID0gYnJlYWtzX2V4dGVuZGVkKCkpICsKICAgICAgICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiR2VudXMiKSArCiAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJCR0MgQ2F0ZWdvcnkiLCB2YWx1ZXMgPSBjYXRfY29sb3JzKSArCiAgICAgICAgY29vcmRfZmxpcCgpICsKICAgICAgICB0aGVtZV9idygpICsgCiAgICAgICAgZ2d0aXRsZSgiKGFsbCBnZW5lcmEpIikKcF9hbGwKZ2dzYXZlKCIuL2ZpZ3Mvc3ZnL2dlbnVzX2NvdW50c19hbGwuc3ZnIiwgcF9hbGwsIGRldmljZSA9ICJzdmciKQpnZ3NhdmUoIi4vZmlncy9wbmcvZ2VudXNfY291bnRzX2FsbC5wbmciLCBwX2FsbCwgZGV2aWNlID0gInBuZyIpCgpgYGAKCmBgYHtyfQpjeWFub19ub2hpdHMgPC0gcmVhZF90c3YoJ2RhdGEvbmNiaV9jeWFub19ub2hpdF9hY2NzLnR4dCcsIGNvbF9uYW1lcz1jKCdhY2Nlc3Npb25faWQnKSkgJT4lIAogICAgbGVmdF9qb2luKGN5YW5vX2FzbV90YXgsIGJ5ID0gam9pbl9ieShhY2Nlc3Npb25faWQgPT0gYXNzZW1ibHlfYWNjZXNzaW9uKSkKCmN5YW5vX25vaGl0X2dlbnVzX2NvdW50cyA8LSBjeWFub19ub2hpdHMgJT4lCiAgICBncm91cF9ieShnZW51cykgJT4lCiAgICBzdW1tYXJpemUobl9ub2hpdHMgPSBuKCkpICU+JQogICAgbXV0YXRlKGdlbnVzID0gcmVwbGFjZV9uYShnZW51cywgIlVuY2xhc3NpZmllZCIpKSAlPiUKICAgIGFycmFuZ2UoZ2VudXMpCgpnZW5vbWVzX2J5X2dlbnVzIDwtIHJlYWRfdHN2KCdkYXRhLzIwMjUtMDItMjUtMTQ0Mi1jeWFub19zbWNfc3JjX2NvdW50c19ieV9nZW51cy50c3YnKQpnZW5vbWVzX2J5X2dlbnVzIDwtIGdlbm9tZXNfYnlfZ2VudXMgJT4lCiAgICBsZWZ0X2pvaW4oY3lhbm9fbm9oaXRfZ2VudXNfY291bnRzLCBieSA9IGpvaW5fYnkodGF4X2dlbnVzID09IGdlbnVzKSkgJT4lCiAgICBtdXRhdGUobl9ub2hpdHMgPSByZXBsYWNlX25hKG5fbm9oaXRzLCAwKSkgJT4lCiAgICBtdXRhdGUodG90X2dlbm9tZXMgPSBuX25vaGl0cyArIG5fc291cmNlcywgLmtlZXAgPSAidW51c2VkIikKCmBgYAoKQkdDcyBwZXIgZ2Vub21lIChsYWJlbGVkIHdpdGggdG90YWwgQkdDcykgd2l0aCBhIHNpZGUgcGFuZWwgZm9yIGdlbm9tZSBjb3VudAoKYGBge3IgZXZhbD1GQUxTRX0KZGZfcGxvdHMgPC0gdGF4X2NvdW50ICU+JSAKICAgIGxlZnRfam9pbihnZW5vbWVzX2J5X2dlbnVzLCBieSA9ICd0YXhfZ2VudXMnKSAlPiUKICAgIG11dGF0ZShiZ2NfZGVucyA9IG51bV9iZ2NzL3RvdF9nZW5vbWVzKSAKCnBfYmdjX2RlbnMgPC0gZGZfcGxvdHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKHRheF9nZW51cywgdz1iZ2NfZGVucykpKSArCiAgICAgICAgZ2VvbV9jb2woYWVzKHkgPSBiZ2NfZGVucywgZmlsbCA9IGdyb3VwKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayhyZXZlcnNlID0gVFJVRSkpICsKICAgICAgICBnZW9tX3RleHQoCiAgICAgICAgICAgIGFlcygKICAgICAgICAgICAgICAgIHkgPSB0b3RfYmdjX2RlbnMsIAogICAgICAgICAgICAgICAgeCA9IHRheF9nZW51cywgCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdCh0b3RfYmdjcywgYmlnLm1hcmsgPSAnLCcsIHRyaW0gPSBUUlVFKQogICAgICAgICAgICApLAogICAgICAgICAgICBkYXRhID0gZGZfcGxvdHMgJT4lIAogICAgICAgICAgICAgICAgZ3JvdXBfYnkodGF4X2dlbnVzKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUodG90X2JnY19kZW5zID0gc3VtKGJnY19kZW5zKSwgdG90X2JnY3MgPSBzdW0obnVtX2JnY3MpKSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3QodGF4X2dlbnVzLCB0b3RfYmdjX2RlbnMsIHRvdF9iZ2NzKSAlPiUgCiAgICAgICAgICAgICAgICBkaXN0aW5jdCgpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMgogICAgICAgICkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkJHQ3MgcGVyIGdlbm9tZSIsIGJyZWFrcyA9IHNlcSgwLDU1LDUpLCBsaW1pdHMgPSBjKDAsNTUpKSArCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkdlbnVzIikgKwogICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQkdDIENhdGVnb3J5IiwgdmFsdWVzID0gY2F0X2NvbG9ycykgKwogICAgICAgIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCiMgcF9iZ2NfZGVucwoKZ2Vub21lX2NvdW50cyA8LSBkZl9wbG90cyAlPiUgCiAgICBncm91cF9ieSh0YXhfZ2VudXMsIHRvdF9nZW5vbWVzKSAlPiUgCiAgICBzdW1tYXJpemUoCiAgICAgICAgdG90X2JnY19kZW5zID0gc3VtKGJnY19kZW5zKSwgCiAgICAgICAgdG90X2JnY3MgPSBzdW0obnVtX2JnY3MpCiAgICApICU+JQogICAgYXJyYW5nZSh0b3RfYmdjX2RlbnMpCmdlbm9tZV9jb3VudHMKCnBfZ2Vub21lX2N0IDwtIGdlbm9tZV9jb3VudHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKHRheF9nZW51cywgdz10b3RfYmdjX2RlbnMpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gdG90X2dlbm9tZXMpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gdHJhbnNmb3JtX3BzZXVkb19sb2coYmFzZT0xMCksIGJyZWFrcyA9IGMoMCwgMSwgNSwgMTAsIDIwLCA1MCwgMTAwLCAyMDAsIDUwMCwgMTAwMCkpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQojIHBfZ2Vub21lX2N0CgpwMSA8LSBwbG90X2dyaWQocF9iZ2NfZGVucywgcF9nZW5vbWVfY3QsIGFsaWduID0gImgiLCByZWxfd2lkdGhzID0gYygyLDEpKQpwMQpnZ3NhdmUoIi4vZmlncy9wbmcvc3BsaXQucG5nIiwgcDEsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIGRldmljZSA9ICJwbmciKQpgYGAKClNhbWUgdGhpbmcgYnV0IGZpbHRlciBmb3IgZ2VuZXJhIHdpdGggYXQgbGVhc3QgNSBnZW5vbWVzCgpgYGB7ciBldmFsPUZBTFNFfQpkZl9wbG90cyA8LSB0YXhfY291bnQgJT4lIAogICAgbGVmdF9qb2luKGdlbm9tZXNfYnlfZ2VudXMsIGJ5ID0gJ3RheF9nZW51cycpICU+JQogICAgbXV0YXRlKGJnY19kZW5zID0gbnVtX2JnY3MvdG90X2dlbm9tZXMpICU+JQogICAgZmlsdGVyKHRvdF9nZW5vbWVzID49IDUpCgpwX2JnY19kZW5zIDwtIGRmX3Bsb3RzICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZmN0X2luZnJlcSh0YXhfZ2VudXMsIHc9YmdjX2RlbnMpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gYmdjX2RlbnMsIGZpbGwgPSBncm91cCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpKSArCiAgICAgICAgZ2VvbV90ZXh0KAogICAgICAgICAgICBhZXMoeSA9IHRvdF9iZ2NfZGVucywgeCA9IHRheF9nZW51cywgbGFiZWwgPSBmb3JtYXQodG90X2JnY3MsIGJpZy5tYXJrID0gJywnLCB0cmltID0gVFJVRSkpLAogICAgICAgICAgICBkYXRhID0gZGZfcGxvdHMgJT4lIGdyb3VwX2J5KHRheF9nZW51cykgJT4lIG11dGF0ZSh0b3RfYmdjX2RlbnMgPSBzdW0oYmdjX2RlbnMpLCB0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpICU+JSBzZWxlY3QodGF4X2dlbnVzLCB0b3RfYmdjX2RlbnMsIHRvdF9iZ2NzKSAlPiUgZGlzdGluY3QoKSwKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpLAogICAgICAgICAgICBoanVzdCA9IC0wLjIKICAgICAgICApICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJCR0NzIHBlciBnZW5vbWUiLCBicmVha3MgPSBzZXEoMCwyNSw1KSwgbGltaXRzID0gYygwLDIwKSkgKwogICAgICAgIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJHZW51cyIpICsKICAgICAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkJHQyBDYXRlZ29yeSIsIHZhbHVlcyA9IGNhdF9jb2xvcnMpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgICAgICAgZ2d0aXRsZSgnPj0gNSBnZW5vbWVzIGluIGdlbnVzJykKIyBwX2JnY19kZW5zCgpnZW5vbWVfY291bnRzIDwtIGRmX3Bsb3RzICU+JSAKICAgIGdyb3VwX2J5KHRheF9nZW51cywgdG90X2dlbm9tZXMpICU+JSAKICAgIHN1bW1hcml6ZSh0b3RfYmdjX2RlbnMgPSBzdW0oYmdjX2RlbnMpLCB0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpICU+JQogICAgYXJyYW5nZSh0b3RfYmdjX2RlbnMpCmdlbm9tZV9jb3VudHMKCnBfZ2Vub21lX2N0IDwtIGdlbm9tZV9jb3VudHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKHRheF9nZW51cywgdz10b3RfYmdjX2RlbnMpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gdG90X2dlbm9tZXMpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6dHJhbnNmb3JtX3BzZXVkb19sb2coYmFzZT0xMCksIGJyZWFrcyA9IGMoMCwgMSwgNSwgMTAsIDIwLCA1MCwgMTAwLCAyMDAsIDUwMCwgMTAwMCkpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQojIHBfZ2Vub21lX2N0CgpwMiA8LSBwbG90X2dyaWQocF9iZ2NfZGVucywgcF9nZW5vbWVfY3QsIGFsaWduID0gImgiLCByZWxfd2lkdGhzID0gYygyLDEpKQpwMgpnZ3NhdmUoIi4vZmlncy9wbmcvc3BsaXRfNWdlbm9tZXMucG5nIiwgcDIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIGRldmljZSA9ICJwbmciKQpgYGAKClNhbWUgYnV0IGluc3RlYWQgZmlsdGVyIGZvciBnZW5lcmEgd2l0aCBhdCBsZWFzdCA1MCBCR0NzCgpgYGB7ciBldmFsPUZBTFNFfQpkZl9wbG90cyA8LSB0YXhfY291bnQgJT4lIAogICAgbGVmdF9qb2luKGdlbm9tZXNfYnlfZ2VudXMsIGJ5ID0gJ3RheF9nZW51cycpICU+JQogICAgbXV0YXRlKGJnY19kZW5zID0gbnVtX2JnY3MvdG90X2dlbm9tZXMpICU+JQogICAgZ3JvdXBfYnkodGF4X2dlbnVzKSAlPiUgbXV0YXRlKHRvdF9iZ2NfZGVucyA9IHN1bShiZ2NfZGVucyksIHRvdF9iZ2NzID0gc3VtKG51bV9iZ2NzKSkgJT4lCiAgICBmaWx0ZXIodG90X2JnY3MgPj0gNTApCgpwX2JnY19kZW5zIDwtIGRmX3Bsb3RzICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZmN0X2luZnJlcSh0YXhfZ2VudXMsIHc9YmdjX2RlbnMpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gYmdjX2RlbnMsIGZpbGwgPSBncm91cCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpKSArCiAgICAgICAgZ2VvbV90ZXh0KAogICAgICAgICAgICBhZXMoeSA9IHRvdF9iZ2NfZGVucywgeCA9IHRheF9nZW51cywgbGFiZWwgPSBmb3JtYXQodG90X2JnY3MsIGJpZy5tYXJrID0gJywnLCB0cmltID0gVFJVRSkpLAogICAgICAgICAgICBkYXRhID0gZGZfcGxvdHMgJT4lIHNlbGVjdCh0YXhfZ2VudXMsIHRvdF9iZ2NfZGVucywgdG90X2JnY3MpICU+JSBkaXN0aW5jdCgpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMgogICAgICAgICkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkJHQ3MgcGVyIGdlbm9tZSIsIGJyZWFrcyA9IHNlcSgwLDI1LDUpLCBsaW1pdHMgPSBjKDAsMjApKSArCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkdlbnVzIikgKwogICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQkdDIENhdGVnb3J5IiwgdmFsdWVzID0gY2F0X2NvbG9ycykgKwogICAgICAgIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICAgICAgICBnZ3RpdGxlKCc+PSA1MCBCR0NzIGluIGdlbnVzJykKIyBwX2JnY19kZW5zCgpnZW5vbWVfY291bnRzIDwtIGRmX3Bsb3RzICU+JSAKICAgIGdyb3VwX2J5KHRheF9nZW51cywgdG90X2dlbm9tZXMpICU+JSAKICAgIHN1bW1hcml6ZSh0b3RfYmdjX2RlbnMgPSBzdW0oYmdjX2RlbnMpLCB0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpICU+JQogICAgYXJyYW5nZSh0b3RfYmdjX2RlbnMpCmdlbm9tZV9jb3VudHMKCnBfZ2Vub21lX2N0IDwtIGdlbm9tZV9jb3VudHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKHRheF9nZW51cywgdz10b3RfYmdjX2RlbnMpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gdG90X2dlbm9tZXMpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6dHJhbnNmb3JtX3BzZXVkb19sb2coYmFzZT0xMCksIGJyZWFrcyA9IGMoMCwgMSwgNSwgMTAsIDIwLCA1MCwgMTAwLCAyMDAsIDUwMCwgMTAwMCkpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQojIHBfZ2Vub21lX2N0CgpwMyA8LSBwbG90X2dyaWQocF9iZ2NfZGVucywgcF9nZW5vbWVfY3QsIGFsaWduID0gImgiLCByZWxfd2lkdGhzID0gYygyLDEpKQpwMwpnZ3NhdmUoIi4vZmlncy9wbmcvc3BsaXRfNTBiZ2NzLnBuZyIsIHAzLCB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBkZXZpY2UgPSAicG5nIikKYGBgCgpQbG90IHRoZSBCR0NzIHBlciBnZW5vbWUgKG5vcm1hbGl6ZWQgdG8gMTAwJSkgYWxvbmdzaWRlIG51bWJlciBvZiBCR0NzIHBlciBnZW51cyBhbmQgZ2Vub21lcyBwZXIgZ2VudXMKCmBgYHtyfQpkZl9wbG90cyA8LSB0YXhfY291bnQgJT4lIAogICAgbGVmdF9qb2luKGdlbm9tZXNfYnlfZ2VudXMsIGJ5ID0gJ3RheF9nZW51cycpICU+JQogICAgbXV0YXRlKGJnY19kZW5zID0gbnVtX2JnY3MvdG90X2dlbm9tZXMpICU+JQogICAgZ3JvdXBfYnkodGF4X2dlbnVzKSAlPiUgbXV0YXRlKHRvdF9iZ2NfZGVucyA9IHN1bShiZ2NfZGVucyksIHRvdF9iZ2NzID0gc3VtKG51bV9iZ2NzKSkKCnBfYmdjX2RlbnMgPC0gZGZfcGxvdHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmV2KHRheF9nZW51cykpKSArCiAgICAgICAgZ2VvbV9jb2woYWVzKHkgPSBiZ2NfZGVucywgZmlsbCA9IGdyb3VwKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHJldmVyc2UgPSBUUlVFKSkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIkJHQyBwcm9wb3J0aW9uIikgKwogICAgICAgIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICJHZW51cyIpICsKICAgICAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkJHQyBDYXRlZ29yeSIsIHZhbHVlcyA9IGNhdF9jb2xvcnMpICsKICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQojIHBfYmdjX2RlbnMKCnBfYmdjX2N0IDwtIGRmX3Bsb3RzICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGZjdF9yZXYodGF4X2dlbnVzKSkpICsKICAgIGdlb21fY29sKGFlcyh5ID0gdG90X2JnY3MpLCBkYXRhID0gZGZfcGxvdHMgJT4lIHNlbGVjdCh0YXhfZ2VudXMsIHRvdF9iZ2NzLCB0b3RfYmdjX2RlbnMpICU+JSBkaXN0aW5jdCgpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICAgICAgbmFtZSA9ICJCR0MgY291bnQiLCAKICAgICAgICB0cmFucyA9IHRyYW5zZm9ybV9wc2V1ZG9fbG9nKGJhc2UgPSAxMCksIAogICAgICAgIGJyZWFrcyA9IGMoMCwgMSwgNSwgMTAsIDIwLCA1MCwgMTAwLCAyMDAsIDUwMCkKICAgICkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHRoZW1lX2J3KCkgKwogICAgdGhlbWUoCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpCiAgICApCgpnZW5vbWVfY291bnRzIDwtIGRmX3Bsb3RzICU+JSAKICAgIGdyb3VwX2J5KHRheF9nZW51cywgdG90X2dlbm9tZXMpICU+JSAKICAgIHN1bW1hcml6ZSh0b3RfYmdjX2RlbnMgPSBzdW0oYmdjX2RlbnMpLCB0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpICU+JQogICAgYXJyYW5nZSh0b3RfYmdjX2RlbnMpCmdlbm9tZV9jb3VudHMKCnBfZ2Vub21lX2N0IDwtIGdlbm9tZV9jb3VudHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmV2KHRheF9nZW51cykpKSArCiAgICAgICAgZ2VvbV9jb2woYWVzKHkgPSB0b3RfZ2Vub21lcykpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICAgICAgICAgIG5hbWUgPSAiR2Vub21lIGNvdW50IiwgCiAgICAgICAgICAgIHRyYW5zPXRyYW5zZm9ybV9wc2V1ZG9fbG9nKGJhc2UgPSAxMCksIAogICAgICAgICAgICBicmVha3MgPSBjKDAsIDEsIDUsIDEwLCAyMCwgNTAsIDEwMCwgMjAwLCA1MDAsIDEwMDApCiAgICAgICAgKSArCiAgICAgICAgY29vcmRfZmxpcCgpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICkKIyBwX2dlbm9tZV9jdAoKcDQgPC0gcGxvdF9ncmlkKHBfYmdjX2RlbnMsIHBfYmdjX2N0LCBwX2dlbm9tZV9jdCwgYWxpZ24gPSAiaCIsIHJlbF93aWR0aHMgPSBjKDMsMSwxKSwgbnJvdz0xKQpwNApnZ3NhdmUoIi4vZmlncy9wbmcvc3BsaXRfcHJvcG9ydGlvbmFsLnBuZyIsIHA0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBkZXZpY2UgPSAicG5nIikKYGBgCgpQbG90IHRoZSBCR0NzIHBlciBnZW5vbWUgKG5vdCBub3JtYWxpemVkIHRvIDEwMCUpIGFsb25nc2lkZSBudW1iZXIgb2YgQkdDcyBwZXIgZ2VudXMgYW5kIGdlbm9tZXMgcGVyIGdlbnVzCgpgYGB7cn0KZGZfcGxvdHMgPC0gdGF4X2NvdW50ICU+JSAKICAgIGxlZnRfam9pbihnZW5vbWVzX2J5X2dlbnVzLCBieSA9ICd0YXhfZ2VudXMnKSAlPiUKICAgIG11dGF0ZShiZ2NfZGVucyA9IG51bV9iZ2NzL3RvdF9nZW5vbWVzKSAlPiUKICAgIGdyb3VwX2J5KHRheF9nZW51cykgJT4lIAogICAgbXV0YXRlKHRvdF9iZ2NfZGVucyA9IHN1bShiZ2NfZGVucyksIHRvdF9iZ2NzID0gc3VtKG51bV9iZ2NzKSkKCnBfYmdjX2RlbnMgPC0gZGZfcGxvdHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcih0YXhfZ2VudXMsIHRvdF9iZ2NfZGVucywgLmRlc2M9VCkpKSArCiAgICAgICAgZ2VvbV9jb2woYWVzKHkgPSBiZ2NfZGVucywgZmlsbCA9IGdyb3VwKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayhyZXZlcnNlID0gVFJVRSkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJCR0NzIHBlciBnZW5vbWUiKSArCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkdlbnVzIikgKwogICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQkdDIENhdGVnb3J5IiwgdmFsdWVzID0gY2F0X2NvbG9ycykgKwogICAgICAgIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocG9zaXRpb24gPSAiaW5zaWRlIikpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbi5pbnNpZGUgPSBjKDAuOTksMC45OSkpCiMgcF9iZ2NfZGVucwoKcF9iZ2NfY3QgPC0gZGZfcGxvdHMgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZmN0X3Jlb3JkZXIodGF4X2dlbnVzLCB0b3RfYmdjX2RlbnMsIC5kZXNjPVQpKSkgKwogICAgICAgIGdlb21fY29sKGFlcyh5ID0gdG90X2JnY3MpLCBkYXRhID0gZGZfcGxvdHMgJT4lIHNlbGVjdCh0YXhfZ2VudXMsIHRvdF9iZ2NzLCB0b3RfYmdjX2RlbnMpICU+JSBkaXN0aW5jdCgpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKAogICAgICAgICAgICBuYW1lID0gIkJHQyBjb3VudCIsIAogICAgICAgICAgICB0cmFucyA9IHRyYW5zZm9ybV9wc2V1ZG9fbG9nKGJhc2UgPSAxMCksIAogICAgICAgICAgICBicmVha3MgPSBjKDAsIDEsIDUsIDEwLCAyMCwgNTAsIDEwMCwgMjAwLCA1MDApCiAgICAgICAgKSArCiAgICAgICAgY29vcmRfZmxpcCgpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICkKICAgIApnZW5vbWVfY291bnRzIDwtIGRmX3Bsb3RzICU+JSAKICAgIGdyb3VwX2J5KHRheF9nZW51cywgdG90X2dlbm9tZXMpICU+JSAKICAgIHN1bW1hcml6ZSh0b3RfYmdjX2RlbnMgPSBzdW0oYmdjX2RlbnMpLCB0b3RfYmdjcyA9IHN1bShudW1fYmdjcykpICU+JQogICAgYXJyYW5nZSh0b3RfYmdjX2RlbnMpCmdlbm9tZV9jb3VudHMKCnBfZ2Vub21lX2N0IDwtIGdlbm9tZV9jb3VudHMgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcih0YXhfZ2VudXMsIHRvdF9iZ2NfZGVucywgLmRlc2M9VCkpKSArCiAgICAgICAgZ2VvbV9jb2woYWVzKHkgPSB0b3RfZ2Vub21lcykpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJHZW5vbWUgY291bnQiLCB0cmFucz1zY2FsZXM6OnRyYW5zZm9ybV9wc2V1ZG9fbG9nKGJhc2U9MTApLCBicmVha3MgPSBjKDAsIDEsIDUsIDEwLCAyMCwgNTAsIDEwMCwgMjAwLCA1MDAsIDEwMDApKSArCiAgICAgICAgY29vcmRfZmxpcCgpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICkKIyBwX2dlbm9tZV9jdAoKcDUgPC0gcGxvdF9ncmlkKHBfYmdjX2RlbnMsIHBfYmdjX2N0LCBwX2dlbm9tZV9jdCwgYWxpZ24gPSAiaCIsIHJlbF93aWR0aHMgPSBjKDMsMSwxKSwgbnJvdz0xKQpkZl9wbG90cwpwNQpnZ3NhdmUoIi4vZmlncy9wbmcvc3BsaXRfdHJpcGxlLnBuZyIsIHA0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBkZXZpY2UgPSAicG5nIikKYGBgCgojIENyZWF0ZSBGaWd1cmUgZm9yIHRoZSBtYW51c2NyaXB0CgpgYGB7cn0KcF9hIDwtIHA1ICsgZ2d0aXRsZSgiIikKCnBfYiA8LSBsdW1wZWRfY2F0ZWdvcnlfY291bnRzCgpwX2MgPC0gcmVnaW9uX2hpc3RfbHVtcGVkICsKICAgIGd1aWRlcyhmaWxsID0gIm5vbmUiKQoKYm90dG9tX3JvdyA8LSBwbG90X2dyaWQocF9iLCBwX2MsIG5yb3cgPSAxLCBsYWJlbHMgPSBjKCJCIiwgIkMiKSwgbGFiZWxfc2l6ZSA9IDEyKQoKZmlnMiA8LSBwbG90X2dyaWQocF9hLCBib3R0b21fcm93LCBucm93ID0gMiwgbGFiZWxzID0gYygiQSIsICIiKSwgbGFiZWxfc2l6ZSA9IDEyLCByZWxfaGVpZ2h0cyA9IGMoMiwxKSkKZmlnMgpnZ3NhdmUoIi4vZmlncy9fZmlnMi5wbmciLCBmaWcyLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcsIGRldmljZSA9ICJwbmciKQpnZ3NhdmUoIi4vZmlncy9fZmlnMi5wZGYiLCBmaWcyLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcsIGRldmljZSA9ICJwZGYiKQpgYGAK